import {
  StructConstructor,
  IStruct,
  formatToString,
  AbstractStruct,
  commonToString,
  ErrorEnum,
  thorwError,
  hashCode
} from "../../shared";
export interface IQueueConstructor<T> extends StructConstructor {
  new (capacity: number): IQueue<T>;
  readonly [Symbol.species]: IQueueConstructor<T>;
  readonly prototype: IQueue<T>;
  of<T>(...datas: T[]): IQueue<T>;
  from<T>(datas: T[]): IQueue<T>;
}
/**
 * 队列，
 * 先进先出
 */
export interface IQueue<T> extends IStruct {
  /**
   * 队列容量
   */
  capacity: number;
  /**
   * 获取对头元素
   */
  front: T;
  /**
   * 在队尾入队
   * @param element 元素
   */
  enQueue(element: T): void;
  /**
   * 在对头出队
   */
  deQueue(): T;
  [Symbol.iterator](): IterableIterator<T>;
  entries(): IterableIterator<[number, T]>;
  keys(): IterableIterator<number>;
  values(): IterableIterator<T>;
}
/**
 * 队列抽象类
 */
export abstract class AbstractQueue<T> extends AbstractStruct
  implements IQueue<T> {
  protected _list: T[] = [];
  abstract enQueue(element: T): void;
  abstract deQueue(): T;
  get size(): number {
    return this._list.length;
  }
  get capacity(): number {
    return this._capacity;
  }
  get hash(): number {
    let res = 0;
    for (const el of this) res += hashCode(el);
    return res;
  }
  get front(): T {
    this.empty && this.structError();
    return this._list[0];
  }
  constructor(private _capacity: number) {
    super();
    this._capacity <= 0 && this.capacityError();
  }
  clear(): void {
    this._list.length = 0;
    super.clear();
  }
  *[Symbol.iterator](): IterableIterator<T> {
    for (const v of this.values()) yield v;
  }
  *keys(): IterableIterator<number> {
    for (const [k] of this.entries()) yield k;
  }
  *values(): IterableIterator<T> {
    for (const [, v] of this.entries()) yield v;
  }
  *entries(): IterableIterator<[number, T]> {
    for (let i = 0; i < this.size; i++) yield [i, this._list[i]];
  }
  toString(): string {
    let s = formatToString(
      this[Symbol.toStringTag],
      ["Capacity", this._capacity],
      ["Size", this.size]
    );
    if (this.size !== 0) {
      s += ",Elements=[";
      for (const val of this) s += commonToString(val) + ",";
      s = s.substr(0, s.length - 1) + "]";
    }
    return s;
  }
  protected checkEnQueue(): void {
    if (this.size >= this._capacity) this.structError();
  }
  protected checkDeQueue(): void {
    if (this.size <= 0) this.structError();
  }
  protected structError(): never {
    this.thorwError(
      formatToString(
        this[Symbol.toStringTag],
        ["Capacity", this._capacity],
        ["Size", this._size]
      )
    );
  }
  protected capacityError(): never {
    this.thorwError(`capacity must greater than 0`);
  }
  /**
   * 抛出越界异常
   * @param index 下标
   */
  protected thorwError(s: string): never {
    thorwError(s, ErrorEnum.range);
  }
}
